// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "src/data_plan_provider.h"

#include <vector>

#include <base/json/json_reader.h>  // NOLINT
#include <base/memory/scoped_ptr.h>  // NOLINT
#include <base/values.h>  // NOLINT
#include <glog/logging.h>  // NOLINT

#include "src/libcurl_http_fetcher.h"

using std::vector;

namespace cashew {

DataPlanProvider::DataPlanProvider(const std::string& carrier)
    : carrier_(carrier), delegate_(NULL),
      fetcher_(CHECK_NOTNULL(new(std::nothrow) LibcurlHttpFetcher())),
      request_in_progress_(false) {
  fetcher_->set_delegate(this);
}

DataPlanProvider::~DataPlanProvider() {
  CancelPendingRequests();
}

const std::string& DataPlanProvider::GetCarrier() const {
  return carrier_;
}

const std::string& DataPlanProvider::GetUsageUrl() const {
  return usage_url_;
}

void DataPlanProvider::SetUsageUrl(const std::string& usage_url) {
  LOG(INFO) << carrier_ << ": SetUsageUrl: url = " << usage_url;
  if (usage_url_ != usage_url) {
    usage_url_ = usage_url;
    CancelPendingRequests();
  }
}

void DataPlanProvider::SetHttpProxy(const std::string& http_proxy) {
  LOG(INFO) << carrier_ << ": SetHttpProxy: http_proxy = " << http_proxy;
  if (http_proxy_ != http_proxy) {
    http_proxy_ = http_proxy;
    CancelPendingRequests();
  }
}

void DataPlanProvider::SetDelegate(DataPlanProviderDelegate *delegate) {
  LOG(INFO) << carrier_ << ": SetDelegate";
  delegate_ = delegate;
  if (delegate_ == NULL) {
    CancelPendingRequests();
  }
}

void DataPlanProvider::SetNameServers(const vector<std::string>& nameservers) {
  fetcher_->set_nameservers(nameservers);
}

bool DataPlanProvider::RequestUsageUpdate() {
  if (usage_url_.empty()) {
    LOG(WARNING) << carrier_ << ": RequestUsageUpdate: no usage url";
    return false;
  }
  if (delegate_ == NULL) {
    LOG(WARNING) << carrier_ << ": RequestUsageUpdate: no delegate";
    return false;
  }
  if (request_in_progress_) {
    LOG(INFO) << carrier_
        << ": RequestUsageUpdate: request already in progress";
    // we'll satisfy this request with the result from the in-flight one
    return true;
  }
  DCHECK(response_buffer_.empty());
  LOG(INFO) << carrier_ << ": RequestUsageUpdate: initiating request";
  request_in_progress_ = true;
  fetcher_->BeginTransfer(usage_url_, http_proxy_);
  return true;
}

// HttpFetcherDelegate methods

void DataPlanProvider::ReceivedBytes(HttpFetcher *fetcher, const char *bytes,
                                     int length) {
  DCHECK(fetcher != NULL);
  DCHECK(bytes != NULL);
  DCHECK_GE(length, 0);
  if (fetcher != fetcher_) {
    LOG(WARNING) << carrier_ << ": ReceivedBytes: wrong fetcher";
    return;
  }
  DCHECK(request_in_progress_);
  LOG(INFO) << carrier_ << ": ReceivedBytes: length = " << length;
  response_buffer_.reserve(response_buffer_.size() + length);
  response_buffer_.insert(response_buffer_.end(), bytes, bytes + length);
}

void DataPlanProvider::TransferComplete(HttpFetcher *fetcher,
                                        bool successful) {
  DCHECK(fetcher != NULL);
  if (fetcher != fetcher_) {
    LOG(WARNING) << carrier_ << ": TransferComplete: wrong fetcher";
    return;
  }
  DCHECK(request_in_progress_);
  DCHECK(delegate_ != NULL);
  LOG(INFO) << carrier_ << ": TransferComplete: result = " << successful;
  if (!successful) {
    LOG(WARNING) << carrier_ << ": TransferComplete: failed: response code = "
        << fetcher_->http_response_code();
    response_buffer_.clear();
    request_in_progress_ = false;
    delegate_->OnRequestComplete(this, false, NULL);
    return;
  }
  LOG(INFO) << carrier_ << ": TransferComplete: success: response code = "
      << fetcher_->http_response_code();

  // carrier usage API has (we hope) given us a JSON-encoded result
  // log it and then parse it, converting it into a tree of Value nodes
  const std::string json_encoded_usage_update(response_buffer_.begin(),
                                              response_buffer_.end());
  response_buffer_.clear();
  request_in_progress_ = false;

  LOG(INFO) << carrier_ << ": TransferComplete: response:\n"
      << json_encoded_usage_update;

  int error_code;
  std::string error_msg;
  scoped_ptr<Value> parsed_usage_update(
      base::JSONReader::ReadAndReturnError(json_encoded_usage_update, true,
                                           &error_code, &error_msg));
  if (parsed_usage_update == NULL) {
    LOG(WARNING) << carrier_ << ": TransferComplete: could not parse json: "
        << "error code " << error_code << ": " << error_msg;
    delegate_->OnRequestComplete(this, false, NULL);
    return;
  }
  LOG(INFO) << carrier_ << ": TransferComplete: successfully parsed json";
  delegate_->OnRequestComplete(this, true, parsed_usage_update.get());
}

void DataPlanProvider::CancelPendingRequests() {
  if (request_in_progress_) {
    fetcher_->TerminateTransfer();
    response_buffer_.clear();
    request_in_progress_ = false;
  }
}

// private methods

}  // namespace cashew
